package org.jackysoft.service.impl;

import java.io.IOException;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.lang.StringUtils;
import org.jackysoft.entity.FieldAccess;
import org.jackysoft.entity.HTMLDocument;
import org.jackysoft.entity.SimpleHtmlElement;
import org.jackysoft.entity.User;
import org.jackysoft.service.AbstractStringIdServiceImpl;
import org.jackysoft.service.FormService;
import org.jackysoft.util.SpringSecurityUtils;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.google.common.collect.Sets;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;

import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;

@Service
@Transactional
public class FormServiceImpl extends AbstractStringIdServiceImpl<HTMLDocument>
		implements FormService {

	/**
	 * 
	 */
	private static final long serialVersionUID = -2544060035672922881L;

	@Override
	public String generateHtmlForm(int actionId, int stepId, Long workflowId,
			String title, Map<Integer, String> nextSteps, byte[] data,
			List<SimpleHtmlElement> htmlValues, FieldAccess stepInfo,
			boolean showOnly, Map<String, Object> transientVar) {
		String formCode = new String(data);
		final Set<String> readonlyFields = Sets.newHashSet();
		if (stepInfo != null) {
			readonlyFields.addAll(stepInfo.getReadonly());
		}
		String base = System.getProperty("base");
		String sbase = System.getProperty("sbase");		

		StringWriter oout = new StringWriter();
		try {
			String html = generateHtml(formCode, nextSteps, actionId,
					workflowId, sbase, base, showOnly, readonlyFields,
					htmlValues);
			Configuration cfg = new Configuration();
			cfg.setTemplateLoader(getConfig().getTemplateLoader());
			cfg.setObjectWrapper(getConfig().getFreemarkerObjectWrapper());
			cfg.setDefaultEncoding("UTF-8");
			Template temp = cfg.getTemplate("showform.ftl");
			temp.setEncoding("UTF-8");
			Map<String, Object> root = new HashMap<String, Object>();
			root.putAll(transientVar);
			root.put("actionId", actionId);
			root.put("stepId", stepId);
			root.put("workflowId", workflowId);
			root.put("base", base);
			root.put("htmlCode", html);
			User user = SpringSecurityUtils.getCurrentUser();
			root.put("localName", user.getLocalName());
			root.put("sbase", sbase);
		
			root.put("formtitle", title);
			StringWriter script = new StringWriter();
			temp.process(root, script);
			script.flush();
			oout.write(script.toString());

		} catch (TemplateException e) {
			log.error(e.toString());
			e.printStackTrace();
		} catch (IOException e) {
			log.error(e.toString());
			e.printStackTrace();
		}

		return (oout.toString());
	}

	/**
	 * @param readonlyFields
	 * 
	 * 
	 * */
	private String generateHtml(String html, Map<Integer, String> nextSteps,
			int actionId, long workflowId, String sbase, String base,
			boolean showOnly, Set<String> readonlyFields,
			List<SimpleHtmlElement> htmlValues) throws IOException {

		/***************************************************
		 * 
		 * 以下形式改用jsoup来修改html元素
		 * 
		 ***************************************************/
		Document doc = Jsoup.parse(html);
		Element head = doc.select("head").first();
		Element body = doc.select("body").first();
		processHtml(doc, readonlyFields, htmlValues);
		String[] scriptsrc = { sbase + "scripts/jquery-1.8.3.js",
				sbase + "scripts/jquery-ui/js/jquery-ui.js",
				sbase + "scripts/jquery-ui/js/i18n/jquery-ui-i18n.js",
				sbase + "scripts/blockUI/jquery.blockUI.js",
				sbase + "scripts/tools.js"

		};
		Element baseElement = doc.createElement("base");
		baseElement.attr("href", base);
		head.appendChild(baseElement);

		head.appendElement("link")
				.attr("rel", "stylesheet")
				.attr("media", "screen")
				.attr("type", "text/css")
				.attr("href",
						sbase + "scripts/jquery-ui/css/flick/jquery-ui.css");
		for (String jsSrc : scriptsrc) {
			head.appendElement("script").attr("language", "javascript")
					.attr("type", "text/javascript").attr("src", jsSrc)
					.text("\t\r\n");
		}
		head.appendElement("meta").attr("http-equiv", "contentType")
				.attr("content", "text/html; charset=utf-8");

		Element ctrldiv = doc.createElement("div").addClass("ctrldiv");
		Element firstChild = body.child(0);
		firstChild.before(ctrldiv);
		if (!showOnly) {
			if (nextSteps != null) {
				for (Integer key : nextSteps.keySet()) {
					ctrldiv.appendElement("button").attr("type", "button")
							.attr("step", key + "")
							.attr("action", actionId + "")
							.attr("entityId", workflowId + "")
							.addClass("gonext").text(nextSteps.get(key));
				}

			}
			ctrldiv.appendElement("button").addClass("closeForm").text("关闭不保存");
		}

		ctrldiv.appendElement("button").addClass("otherAttachment").text("附件");

		ctrldiv.appendElement("div").attr("id", "attachments").text("\r\n\t");

		return doc.html();
	}

	/**
	 * 把doc 里 readonlys的元素设置成 readonly
	 * @param doc dom4j文档
	 * @param readonlys 需要设置成只读的元素名称
	 * @param values 所有页面元素
	 * */
	protected void processHtml(Document doc, Set<String> readonlys,
			List<SimpleHtmlElement> values) {
		Element body = doc.select("body").first();
		Elements inputs = body.select("form input");
		setAttrs(inputs, readonlys, true, values);
		// 取select 元素
		inputs = body.select("form select");
		setAttrs(inputs, readonlys, false, values);
		// 取textarea元素
		inputs = body.select("form textarea");
		setAttrs(inputs, readonlys, true, values);
		// 取button元素
		inputs = body.select("form  button");
		setAttrs(inputs, readonlys, false, Collections.EMPTY_LIST);
	}
	
	/**
	 * 
	 * */
	void setAttrs(Elements inputs, Set<String> readonlys, boolean trimValue,
			List<SimpleHtmlElement> values) {
		for (Iterator<Element> itr = inputs.iterator(); itr.hasNext();) {
			Element input = itr.next();
			String name = StringUtils.trimToEmpty(input.attr("name"));

			for (SimpleHtmlElement se : values) {
				String tagName = input.tagName();
				String sName = StringUtils.trimToEmpty(se.getName());
				if ("input".equalsIgnoreCase(tagName)) {
					if (sName.equalsIgnoreCase(name)) {
						String type = input.attr("type");
						if (StringUtils.trimToNull(type) == null)
							type = "text";

						if ("radio".equalsIgnoreCase(type)
								|| "checkbox".equalsIgnoreCase(type)) {
							if (StringUtils.isNotEmpty(se.getChecked()))
								input.attr("checked", "true");
						}
					}

				}
				if (sName.equalsIgnoreCase(name)) {
					input.val(StringUtils.trimToEmpty(se.getValue()));
				}

			}

			if (trimValue)
				input.val(StringUtils.trimToEmpty(input.val()));
			for (String rname : readonlys) {
				if (rname.equalsIgnoreCase(name))
					input.attr("disabled", "disabled");
			}

		}
	}

	@Override
	public String extractHtmlElementsToJson(String html) throws Exception {
		Collection<SimpleHtmlElement> elements = extractHtmlElements(html);
		GsonBuilder builder = new GsonBuilder();
		builder.excludeFieldsWithoutExposeAnnotation();
		Gson gson = builder.create();
		StringWriter writer = new StringWriter();
		gson.toJson(elements, writer);
		return writer.toString();
	}

	private Collection<SimpleHtmlElement> extractHtmlElements(String html) {
		Collection<SimpleHtmlElement> elements = new ArrayList<SimpleHtmlElement>();
		Document doc = Jsoup.parse(html);
		Element body = doc.select("body").first();
		Elements inputs = body.select("form input");
		// 先取input 元素 type=text
		for (Iterator<Element> itr = inputs.iterator(); itr.hasNext();) {
			Element input = itr.next();
			String type = input.attr("type");
			if (type == null)
				type = "text";
			if (INPUT_TYPES.contains(type.toLowerCase())) {
				SimpleHtmlElement el = new SimpleHtmlElement();
				el.setId(input.attr("id"));
				el.setName(input.attr("name"));
				el.setTitle(input.attr("title"));
				el.setChecked(input.attr("checked"));
				el.setEnabled(input.attr("enabled"));
				el.setDisabled(input.attr("disabled"));
				elements.add(el);
			}
		}
		// 取select 元素
		inputs = body.select("form select");
		for (Iterator<Element> itr = inputs.iterator(); itr.hasNext();) {
			Element select = itr.next();
			SimpleHtmlElement el = new SimpleHtmlElement();
			el.setId(select.attr("id"));
			el.setName(select.attr("name"));
			el.setTitle(select.attr("title"));
			el.setChecked(select.attr("checked"));
			el.setEnabled(select.attr("enabled"));
			el.setDisabled(select.attr("disabled"));
			elements.add(el);
		}
		// 取textarea元素
		inputs = body.select("form textarea");
		for (Iterator<Element> itr = inputs.iterator(); itr.hasNext();) {
			Element select = itr.next();
			SimpleHtmlElement el = new SimpleHtmlElement();
			el.setId(select.attr("id"));
			el.setName(select.attr("name"));
			el.setTitle(select.attr("title"));
			el.setChecked(select.attr("checked"));
			el.setEnabled(select.attr("enabled"));
			el.setDisabled(select.attr("disabled"));
			elements.add(el);
		}
		// 取button元素
		inputs = body.select("form  button");
		for (Iterator<Element> itr = inputs.iterator(); itr.hasNext();) {
			Element select = itr.next();
			SimpleHtmlElement el = new SimpleHtmlElement();
			el.setId(select.attr("id"));
			el.setName(select.attr("name"));
			el.setTitle(select.attr("title"));
			el.setChecked(select.attr("checked"));
			el.setEnabled(select.attr("enabled"));
			el.setDisabled(select.attr("disabled"));
			elements.add(el);
		}
		return elements;
	}

	@Override
	public String generateHtmlForm(long workflowId, byte[] data,
			List<SimpleHtmlElement> htmlValues) {
		/***************************************************
		 * 
		 * 以下形式改用jsoup来修改html元素
		 * 
		 ***************************************************/
		String base = System.getProperty("base");
		String sbase = System.getProperty("sbase");
		String mainweb = System.getProperty("myappbase");
		Document doc = Jsoup.parse(new String(data));
		Element head = doc.select("head").first();
		Element body = doc.select("body").first();
		Set<String> readonlyFields = Sets.newHashSet();
		for (SimpleHtmlElement sh : htmlValues) {
			readonlyFields.add(sh.getName());
		}
		processHtml(doc, readonlyFields, htmlValues);
		String[] scriptsrc = { sbase + "scripts/jquery-1.8.3.js",
				sbase + "scripts/jquery-ui/js/jquery-ui-min.js",
				sbase + "scripts/jquery-ui/ui/i18n/jquery-ui-i18n.js",
				sbase + "scripts/blockUI/jquery.blockUI.js",
				sbase + "scripts/tools.js"

		};
		Element baseElement = doc.createElement("base");
		baseElement.attr("href", base);
		head.appendChild(baseElement);

		head.appendElement("link")
				.attr("rel", "stylesheet")
				.attr("media", "screen")
				.attr("type", "text/css")
				.attr("href",
						sbase + "scripts/jquery-ui/css/flick/jquery-ui.css");
		for (String jsSrc : scriptsrc) {
			head.appendElement("script").attr("language", "javascript")
					.attr("type", "text/javascript").attr("src", jsSrc)
					.text("\t\r\n");
		}
		head.appendElement("meta").attr("http-equiv", "contentType")
				.attr("content", "text/html; charset=utf-8");
		StringWriter out = new StringWriter();

		try {
			Configuration cfg = new Configuration();
			cfg.setTemplateLoader(getConfig().getTemplateLoader());
			cfg.setObjectWrapper(getConfig().getFreemarkerObjectWrapper());
			cfg.setDefaultEncoding("UTF-8");
			Template temp = cfg.getTemplate("showform.ftl");
			temp.setEncoding("UTF-8");
			Map<String, Object> root = new HashMap<String, Object>();

			root.put("workflowId", workflowId);
			root.put("base", base);
			root.put("htmlCode", doc.html());
			User user = SpringSecurityUtils.getCurrentUser();
			root.put("localName", user.getLocalName());
			root.put("sbase", sbase);
			root.put("myappbase", mainweb);
    		temp.process(root, out);
		
		} catch (TemplateException e) {
			log.error(e.toString());
			e.printStackTrace();
		} catch (IOException e) {
			log.error(e.toString());
			e.printStackTrace();
		}

		return out.toString();
	}

}
